Shader1_Unity 默认渲染管线 与 .cginc

前言

首先要有一个概念,即 渲染管线 和 Shader 是对应的,同一个 Shader 放到不同的渲染管线里可能就会出现问题。

一般情况下我们使用的渲染管线都是 Unity 默认的渲染管线,使用的 Shader 默认也 Unity 提供相对应的默认 Shader,这里我们要继续使用 Unity 默认渲染管线,就最好在原有默认 Shader 的基础上进行修改,这样的话之前 默认Shader 的功能也不会丢失,也不容易出问题。

即对应不同种类 GameObject 的默认 Shader 相当于我们写 Shader 的模板,在初期很多东西都不了解的情况下,我们需要修改的东西很少很少,而 .cginc文件 相当于一个公共的库,使用 .cginc 可以把重复的部分单独抽出来,这样之后再写会节省很多时间,一些通用的图形学公式也可以放到 .cginc,使用的时候直接调用就可以了。

获取 Unity 内置 Shader

首先是下载对应的 Unity 默认 Shader,我使用的是18.4.3f1,是长期支持的版本之一。

使用类似 VS Code 的文本工作环境打开整个文件夹,找到 UI-Deafult,默认情况下我们新建 UI/Image就是使用的这个Shader。

集合到 .cginc

这里我们新建一个 UIDefault.cginc 文件,用于保存默认的 UI-Default 模板,基本就是把 默认的 UI Shader 复制了过来。下面是一些解释,大家可以先略过,等想看的时候回头再来看。

  • #include 了 .cginc,是为了后续调用对应的方法或者值,可以进到这两个文件中查看,在上一步下载到的目录中可以找到具体代码。
  • #pragma 是定义宏,在编译的时候可以选择合适的代码编译
  • 两个 struct 直接 copy 过来,对应使用。
  • 接下来是 UI 通用的一些属性
  • 把 vert 和 frag 更改了名字,并在 frag 中修改了直接去颜色的方法,改成自定义的 ProcessColor,这样的话可以在外部进行自定义该方法,实现不同的效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include "UnityCG.cginc"
#include "UnityUI.cginc"
// “multi_compile_instancing”会使你的Shader生成两个Variant,其中一个定义了Shader关键字INSTANCING_ON,另外一个没有定义此关键字。
// 除了这个#pragma指令,下面所列其他的修改都是使用了在UnityInstancing.cginc里定义的宏(此cginc文件位于Unity_Install_Dir\Editor\Data\CGIncludes)。
// 取决于关键字INSTANCING_ON是否被定义,这些宏将展开为不同的代码。
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP

// 其实是,传说中的 a2v 就是从 Unity App 传过来的数据
struct appdata_t
{
// 网格顶点 (一个 UIImage ,其实是两个三角形组成的矩形网格)
float4 vertex : POSITION;
// 顶点的颜色(一般就是白色)
float4 color : COLOR;
// 纹理坐标(俗称 uv)
float2 texcoord : TEXCOORD0;
// 用于在Vertex Shader输入 / 输出结构中定义一个语义为SV_InstanceID的元素。
UNITY_VERTEX_INPUT_INSTANCE_ID
};

// 这个应该熟悉了,是 Vertex Shader to Fragment Shader Data
struct v2f
{
// 这个是三维转换为屏幕坐标之后的顶点
float4 vertex : SV_POSITION;
// 顶点颜色
fixed4 color : COLOR;
// 纹理坐标(UV)
float2 texcoord : TEXCOORD0;
// 世界坐标系其实是 三维的 vertex 坐标系
float4 worldPosition : TEXCOORD1;
// 不用管
UNITY_VERTEX_OUTPUT_STEREO
};

// 用于计算的纹理 (UIImage 的 Sprite)
sampler2D _MainTex;
fixed4 _Color;
// 用于采样叠加的纹理(不用管,是固定套路)
fixed4 _TextureSampleAdd;
float4 _MainTex_ST;
// 裁剪区域(不用管)
float4 _ClipRect;

// 顶点着色器(固定套路)
v2f vert_default(appdata_t IN)
{
v2f OUT;
// 这个宏必须在Vertex Shader的最开始调用,如果你需要在Fragment Shader里访问Instanced属性,
// 则需要在Fragment Shader的开始也用一下。这个宏的目的在于让Instance ID在Shader函数里也能够被访问到。
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

// 缓存三维顶点坐标
OUT.worldPosition = IN.vertex;

// 三维顶点坐标变换二维顶点坐标
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _MainTex);

OUT.texcoord = IN.texcoord;

OUT.color = IN.color * _Color;
return OUT;
}


fixed4 ProcessColor(v2f IN);

// 片元着色器
fixed4 frag_default(v2f IN)
{
// 生成灰度颜色(不用考虑 lerp 只看 float4里的就好
fixed4 color = (ProcessColor(IN)+ _TextureSampleAdd) * IN.color;

// 下边就是固定套路
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif

#ifdef UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif

return color;
}

// 各种自定义方法

// 3x3 滤波
float4 filter(float3x3 filter,float _BlurOffset, sampler2D tex, float2 coord)
{
float4 outCol = float4(0,0,0,0);
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
//计算采样点,得到当前像素附近的像素的坐标
float2 newUV= float2(coord.x + (i-1)*_BlurOffset, coord.y + (j-1)*_BlurOffset);
//采样并乘以滤波器权重,然后累加
outCol += tex2D(tex, newUV) * filter[i][j];
//是否处理透明度
//outCol.a = tex2D(tex, coord).a;
}
}
return outCol;
}

具体使用

具体使用会讲解一个案例来使用,blur 效果。

0%